package repository

import (
	"context"
	"go-admin/server/internal/domain"
	"go-admin/server/internal/repository/dao"
	"go.opentelemetry.io/otel"
	"time"
)

type MenuRepository interface {
	GetMenuData(ctx context.Context) ([]domain.Menu, error)
	GetLeftMenu(ctx context.Context, menuIds []int64) ([]domain.Menu, error)
	SearchMenu(ctx context.Context, dm domain.Menu) ([]domain.Menu, error)
	AddMenu(ctx context.Context, dm domain.Menu) error
	EditMenu(ctx context.Context, dm domain.Menu) error
	DelMenu(ctx context.Context, dm domain.Menu) error
}

type menuRepository struct {
	dao dao.MenuDao
}

func NewMenuRepository(dao dao.MenuDao) MenuRepository {
	return &menuRepository{
		dao: dao,
	}
}

// 获取菜单数据
func (m *menuRepository) GetMenuData(ctx context.Context) ([]domain.Menu, error) {
	data, err := m.dao.GetMenuDataTree(ctx)
	if err != nil {
		return []domain.Menu{}, err
	}
	var result []domain.Menu
	for _, menuData := range data {
		menu := m.entityToDomain(menuData)
		result = append(result, menu)
	}
	return result, nil
}

func (m *menuRepository) GetLeftMenu(ctx context.Context, menuIds []int64) ([]domain.Menu, error) {
	ctx, span := otel.Tracer("左侧菜单").Start(ctx, "Repository层")
	defer span.End()
	data, err := m.dao.GetLeftMenu(ctx, menuIds)
	if err != nil {
		return []domain.Menu{}, err
	}
	var result []domain.Menu
	for _, menuData := range data {
		menu := m.entityToDomain(menuData)
		result = append(result, menu)
	}
	return result, nil
}

func (m *menuRepository) SearchMenu(ctx context.Context, dm domain.Menu) ([]domain.Menu, error) {
	data, err := m.dao.Search(ctx, m.domainToEntity(dm))
	if err != nil {
		return []domain.Menu{}, err
	}
	var result []domain.Menu
	for _, menuData := range data {
		menu := m.entityToDomain(menuData)
		result = append(result, menu)
	}
	return result, nil
}

func (m *menuRepository) AddMenu(ctx context.Context, dm domain.Menu) error {
	return m.dao.Insert(ctx, m.domainToEntity(dm))
}

func (m *menuRepository) EditMenu(ctx context.Context, dm domain.Menu) error {
	return m.dao.Update(ctx, m.domainToEntity(dm))
}

func (m *menuRepository) DelMenu(ctx context.Context, dm domain.Menu) error {
	return m.dao.Del(ctx, m.domainToEntity(dm))
}

func (m *menuRepository) domainToEntity(md domain.Menu) dao.Menu {
	return dao.Menu{
		ID:         md.MenuID,
		Pid:        md.ParentID,
		MenuLevel:  md.Level,
		MenuName:   md.Name,
		MenuPath:   md.Path,
		MenuUrl:    md.Permission,
		MenuSort:   md.SortOrder,
		JumpUrl:    md.FrontRoute,
		MenuTitle:  md.Title,
		CreateTime: time.Now().UnixMilli(),
		UpdateTime: time.Now().UnixMilli(),
	}
}

func (m *menuRepository) entityToDomain(md dao.Menu) domain.Menu {
	return domain.Menu{
		MenuID:     md.ID,
		Level:      md.MenuLevel,
		Name:       md.MenuName,
		Path:       md.MenuPath,
		FrontRoute: md.JumpUrl,
		Permission: md.MenuUrl,
		SortOrder:  md.MenuSort,
		ParentID:   md.Pid,
		MenuIsShow: 1,
		Title:      md.MenuTitle,
		CreateTime: time.UnixMilli(md.CreateTime),
		UpdateTime: time.UnixMilli(md.UpdateTime),
		Children:   m.convertMenus(md.Children),
	}
}

func (m *menuRepository) convertMenus(daoMenus []dao.Menu) []domain.Menu {
	var domainMenus []domain.Menu
	if daoMenus == nil {
		return []domain.Menu{}
	}
	for _, daoMenu := range daoMenus {
		domainMenu := domain.Menu{
			MenuID:     daoMenu.ID,
			FrontRoute: daoMenu.JumpUrl,
			Level:      daoMenu.MenuLevel,
			Name:       daoMenu.MenuName,
			Path:       daoMenu.MenuPath,
			ParentID:   daoMenu.Pid,
			Permission: daoMenu.MenuUrl,
			SortOrder:  daoMenu.MenuSort,
			MenuIsShow: 1,
			Title:      daoMenu.MenuTitle,
			UpdateTime: time.UnixMilli(daoMenu.UpdateTime),
			CreateTime: time.UnixMilli(daoMenu.UpdateTime),
		}
		domainMenu.Children = m.convertMenus(daoMenu.Children)
		domainMenus = append(domainMenus, domainMenu)
	}
	return domainMenus
}
